List et Map
Objectif : typer vos collections pour la sûreté et l'auto-complétion.
Pourquoi des types génériques ?
List<T>garantit que chaque élément est de typeT.Map<K,V>garantit le type des clés (K) et des valeurs (V).- Moins d'erreurs runtime, meilleure complétion IDE et tri/filtrage sûrs.
List
final List<String> fruits = ['pomme', 'poire'];
fruits.add('mangue');
// Littéral typé sans répétition
final fruits2 = <String>['pomme', 'poire'];
// Parcours typé
for (final fruit in fruits2) {
print(fruit.toUpperCase());
}
Points clés :
- Préférez déclarer le type à gauche ou via le littéral
<T> [...]. List.unmodifiable([...])pour une vue immuable ;const []pour compile-time constants.list.where(...),map(...),sort(...)restent typés et sûrs.
Exemples supplémentaires (List) :
final notes = <int>[12, 8, 15];
// Ajout d'un élément
notes.add(10);
// Accès par index
final premiere = notes[0]; // 12
// Remplacement
notes[1] = 9;
final noms = <String>['Ana', 'Ben', 'Chloé'];
// Filtrer
final avecA = noms.where((n) => n.startsWith('A')).toList();
// Transformer
final majuscules = noms.map((n) => n.toUpperCase()).toList();
final scores = <int>[5, 1, 3];
// Trier (modifie la liste)
scores.sort();
// Vérifier l'existence d'un élément
final contient3 = scores.contains(3); // true
Exemple de transformations typées (where, map, fold) :
final notes = <int>[12, 8, 15, 9];
// Garder les notes >= 10, les convertir en /20, puis sommer
final totalSur20 = notes
.where((n) => n >= 10)
.map((n) => n * 2)
.fold<int>(0, (acc, n) => acc + n);
// totalSur20 = 54 (12*2 + 15*2)
Map
final Map<String, int> scores = {
'alice': 10,
'bob': 7,
};
scores['carol'] = 12;
// Littéral typé
final scores2 = <String, int>{
'dan': 5,
};
// Lecture sécurisée avec valeur par défaut
final pointsAlice = scores['alice'] ?? 0;
Points clés :
- Typage des clés et valeurs :
<String, int>{ ... }. putIfAbsent(key, () => value)pour insérer si absent.- Itérations :
map.entries,map.keys,map.values.
Exemples map.entries, map.keys, map.values :
final scores = <String, int>{
'alice': 10,
'bob': 7,
};
// entries : paires clé/valeur
for (final entry in scores.entries) {
print('${entry.key} -> ${entry.value}');
}
// keys : toutes les clés
for (final name in scores.keys) {
print(name.toUpperCase());
}
// values : toutes les valeurs
final total = scores.values.reduce((a, b) => a + b);
print(total); // 17
Exemples putIfAbsent :
final stock = <String, int>{
'pomme': 3,
};
// Insère si absent
stock.putIfAbsent('poire', () => 5);
// N'écrase pas si présent
stock.putIfAbsent('pomme', () => 99);
// stock => {'pomme': 3, 'poire': 5}
final visites = <String, int>{};
void visite(String page) {
// Initialise à 0 si absent, puis incrémente
final current = visites.putIfAbsent(page, () => 0);
visites[page] = current + 1;
}
visite('/accueil');
visite('/accueil');
// visites => {'/accueil': 2}
final cache = <String, String>{};
String chargerProfil(String id) {
// La fonction n'est appelée que si la clé est absente
return cache.putIfAbsent(id, () {
return 'profil_$id';
});
}
final a = chargerProfil('u1');
final b = chargerProfil('u1');
// a et b viennent du cache, la fonction n'est exécutée qu'une fois
Spreads et collection-if/for (rappel)
final base = <int>[1, 2];
final extended = <int>[0, ...base, 3];
final maybeNull = <int>[];
final safe = <int>[...?, maybeNull]; // n'ajoute rien si null
final filtered = [
for (final x in extended)
if (x.isEven) x,
];
Casts sur collections
final raw = <dynamic>['a', 'b'];
final strings = raw.cast<String>(); // vérifie les éléments à l'usage
Bonnes pratiques
- Évitez
ListouMapsans type : utilisezList<T>/Map<K,V>. - Préférez les transformations typées (
map,where,fold) plutôt que des boucles qui mélangent les types. - Utilisez
cast<T>()avec parcimonie ; idéalement, construisez vos collections déjà typées (ex. viafromJson).